﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Xna_Shootr
{
    public enum ForceDirection { Up, Down, Left, Right, None };

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D bgTexture;
        Rectangle viewportRect;
        SpaceShipObject spaceShip, spaceShip2; // spaceShip2 for testing collision
        KeyboardState preKeyState = Keyboard.GetState();
        const float UNIT_FORCE = 0.2f;
        const uint TYPES_OF_BULLETS = 3;
        Texture2D[] bulletSprites;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // load background texture
            bgTexture = Content.Load<Texture2D>("Sprites\\Background");
            viewportRect = new Rectangle(0, 0, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height);

            bulletSprites = new Texture2D[TYPES_OF_BULLETS];
            bulletSprites[0] = Content.Load<Texture2D>("Sprites\\Bullet_1");
            bulletSprites[1] = Content.Load<Texture2D>("Sprites\\Bullet_2");
            bulletSprites[2] = Content.Load<Texture2D>("Sprites\\Bullet_3");

            // create space ship
            spaceShip = new SpaceShipObject(Content.Load<Texture2D>("Sprites\\ufo"),
                                            bulletSprites,
                                            0, // default weapon type
                                            new Vector2(viewportRect.Width/2, viewportRect.Height/2));
            spaceShip2 = new SpaceShipObject(Content.Load<Texture2D>("Sprites\\ufo"),
                                             bulletSprites,
                                             0, // default weapon type
                                             new Vector2(viewportRect.Width / 4, viewportRect.Height / 2));
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        private void HandleKeyboardStateChange()
        {
            float x = 0.0f , y = 0.0f;
            KeyboardState keyState = Keyboard.GetState();

            // handle space ship direction change here
            if (keyState.IsKeyDown(Keys.Up))
            {
                y += -UNIT_FORCE;
            }
            if (keyState.IsKeyDown(Keys.Down))
            {
                y += UNIT_FORCE;
            }
            if (keyState.IsKeyDown(Keys.Left))
            {
                x += -UNIT_FORCE;
            }
            if (keyState.IsKeyDown(Keys.Right))
            {
                x += UNIT_FORCE;
            }

            spaceShip.Push(new Vector2(x, y));

            // weapon selection
            if (keyState.IsKeyDown(Keys.D1) && preKeyState.IsKeyUp(Keys.D1))
            {
                spaceShip.WeaponSwitch(0);
            }
            else if (keyState.IsKeyDown(Keys.D2) && preKeyState.IsKeyUp(Keys.D2))
            {
                spaceShip.WeaponSwitch(1);
            }
            else if (keyState.IsKeyDown(Keys.D3) && preKeyState.IsKeyUp(Keys.D3))
            {
                spaceShip.WeaponSwitch(2);
            }

#if DEBUG
            // reset space ship position for debug purpose
            if (keyState.IsKeyDown(Keys.Escape))
            {
                spaceShip.Position = new Vector2(viewportRect.Width / 2 - spaceShip.Center.X, viewportRect.Height / 2 - spaceShip.Center.Y);
            }
#endif
            if (keyState.IsKeyDown(Keys.Space) && preKeyState.IsKeyUp(Keys.Space))
            {
                spaceShip.Fire();
            }

            preKeyState = Keyboard.GetState();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            HandleKeyboardStateChange();
            
            spaceShip.OutOfBoundCheck(viewportRect);
            spaceShip.Rotation = (float)Math.Atan2(spaceShip.Velocity.Y, spaceShip.Velocity.X) + MathHelper.PiOver2;
            spaceShip.Update(gameTime);

            foreach (Bullet b in Bullet.ActivatedBullets)
            {
                b.Update(gameTime);
                b.OutOfBoundCheck(viewportRect); // mark dead bullets if they are out of bound
            }

            // we can't use foreach loop to remove items in a List
            // so here we use C# 3.0 RemoveAll function with a Predicate
            Bullet.ActivatedBullets.RemoveAll(bullet => bullet.Alive == false);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            spriteBatch.Draw(bgTexture, viewportRect, Color.White);
            spaceShip2.Draw(spriteBatch);
            
            if (!spaceShip.DetectCollision(spaceShip2))
                spaceShip.Draw(spriteBatch);
            
            foreach (IDrawable b in Bullet.ActivatedBullets)
            {
                b.Draw(spriteBatch);
            }

            spriteBatch.End();
            base.Draw(gameTime);           
        }
    }
}
